/*
 * i2c_csp.c - I2C host driver code for LomboTech
 * i2c hardware operation
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef ___I2C___CSP__C___
#define ___I2C___CSP__C___

#include <mach/csp.h>
#include "i2c_const.h"

/* #define ENABLE_TRACE */
#include <mach/debug.h>
#undef MOD_NAME
#define MOD_NAME	"I2C"

/******************************************************************************
 * base controller
 *****************************************************************************/
/**
 * csp_i2c_enable - enable/disable the i2c controller
 * @base_addr:	base address for I2CC
 * @enable:	0, disable; 1, enable
 */
void csp_i2c_enable(void *base_addr, u32 enable)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_enable_t enable_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, enable);

	enable_mask.val = READREG32(&i2cc->enable);
	enable_mask.bits.en = enable;

	WRITEREG32(&i2cc->enable, enable_mask.val);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * base configure
 *****************************************************************************/

/*
 * operate mode

 */
/**
 * csp_i2c_auto_enable - enable/disable the i2c auto mode
 * @base_addr:	base address for I2CC
 * @enable:	0, disable; 1, enable
 */
void csp_i2c_auto_enable(void *base_addr, u32 enable)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, enable);

	ctl_mask.val = READREG32(&i2cc->ctl);
	ctl_mask.bits.auto_en = enable;

	WRITEREG32(&i2cc->ctl, ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_pp_enable - enable/disable the i2c pp mode
 * @base_addr:	base address for I2CC
 * @enable:	0, disable; 1, enable
 */
void csp_i2c_pp_enable(void *base_addr, u32 enable)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, enable);

	ctl_mask.val = READREG32(&i2cc->ctl);
	ctl_mask.bits.hs_pp_en = enable;

	WRITEREG32(&i2cc->ctl, ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_auto_start_enable - enable/disable the i2c auto start
 * @base_addr:	base address for I2CC
 * @enable:	0, idle; 1, run
 */
void csp_i2c_auto_start_enable(void *base_addr, u32 enable)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_enable_t enable_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, enable);

	enable_mask.val = READREG32(&i2cc->enable);
	enable_mask.bits.auto_start = enable;

	WRITEREG32(&i2cc->enable, enable_mask.val);

	PRT_TRACE_END("\n");
}

/*
 * Speed mode
 */

/**
 * csp_i2c_set_speed_mode - set the master speed mode
 * @base_addr:	base address for I2CC
 * @speed_mode:	speed mode, can be I2C_SS_MODE, I2C_FS_MODE, I2C_HS_MODE
 */
void csp_i2c_set_speed_mode(void *base_addr, u32 speed_mode)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,speed_mode=%d\n",
			base_addr, speed_mode);

	ctl_mask.val = READREG32(&i2cc->ctl);
	ctl_mask.bits.speed = speed_mode;
	WRITEREG32(&i2cc->ctl, ctl_mask.val);

	PRT_TRACE_END("\n");
}

#if 1
/**
 * csp_i2c_enable_hs_sclpp - enable/disable HS SCL push-pull
 * @base_addr:	base address for I2CC
 * @enable:	0, disable; 1, enable
 */
void csp_i2c_enable_hs_sclpp(void *base_addr, u32 enable)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, enable);

	ctl_mask.val = READREG32(&i2cc->ctl);
	ctl_mask.bits.hs_pp_en = enable;
	WRITEREG32(&i2cc->ctl, ctl_mask.val);

	PRT_TRACE_END("\n");
}
#endif

/*
 * INT_behavior
 */

/******************************************************************************
 * Timing
 *****************************************************************************/
/**
 * csp_i2c_set_ss_baudrate - set the SS baudrate
 * @base_addr:	base address for I2CC
 * @h_cnt:	SCL HIGH duration in ic clock cycle cnt
 * @l_cnt:	SCL LOW duration in ic clock cycle cnt
 */
void csp_i2c_set_ss_baudrate(void *base_addr, u32 h_cnt, u32 l_cnt)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_ss_div_t ss_div_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,h_cnt=%d,l_cnt=%d\n",
			base_addr, h_cnt, l_cnt);

	ss_div_mask.val = READREG32(&i2cc->ss_div);
	ss_div_mask.bits.hcnt = h_cnt;
	ss_div_mask.bits.lcnt = l_cnt;
	WRITEREG32(&i2cc->ss_div, ss_div_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_set_fs_baudrate - set the SS/FS+ baudrate
 * @base_addr:	base address for I2CC
 * @h_cnt:	SCL HIGH duration in ic clock cycle cnt
 * @l_cnt:	SCL LOW duration in ic clock cycle cnt
 */
void csp_i2c_set_fs_baudrate(void *base_addr, u32 h_cnt, u32 l_cnt)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_ss_div_t ss_div_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,h_cnt=%d,l_cnt=%d\n",
			base_addr, h_cnt, l_cnt);

	ss_div_mask.val = READREG32(&i2cc->ss_div);
	ss_div_mask.bits.hcnt = h_cnt;
	ss_div_mask.bits.lcnt = l_cnt;
	WRITEREG32(&i2cc->ss_div, ss_div_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_set_hs_baudrate - set the HS baudrate
 * @base_addr:	base address for I2CC
 * @h_cnt:	SCL HIGH duration in ic clock cycle cnt
 * @l_cnt:	SCL LOW duration in ic clock cycle cnt
 */
void csp_i2c_set_hs_baudrate(void *base_addr, u32 h_cnt, u32 l_cnt)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_hs_div_t hs_div_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,h_cnt=%d,l_cnt=%d\n",
			base_addr, h_cnt, l_cnt);

	hs_div_mask.val = READREG32(&i2cc->hs_div);
	hs_div_mask.bits.hcnt = h_cnt;
	hs_div_mask.bits.lcnt = l_cnt;
	WRITEREG32(&i2cc->hs_div, hs_div_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_cfg_spk_len - set the spk length
 * @base_addr:	base address for I2CC
 * @ss_spk_len:	SS/FS/FS+ spike length in ic clock cycle cnt
 * @hs_spk_len:	HS spike length in ic clock cycle cnt
 */
void csp_i2c_cfg_spk_len(void *base_addr, u32 ss_spk_len, u32 hs_spk_len)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_spk_len_t spk_len_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,ss_spk_len=%d,hs_spk_len=%d\n",
			base_addr, ss_spk_len, hs_spk_len);

	spk_len_mask.val = READREG32(&i2cc->spk_len);
	spk_len_mask.bits.ss_spklen = ss_spk_len;
	spk_len_mask.bits.hs_spklen = hs_spk_len;
	WRITEREG32(&i2cc->spk_len, spk_len_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_set_timing - set TX and RX hold time on SDA
 * @base_addr:	base address for I2CC
 * @tx_ht:	TX hold time on SDA in ic clock cycle cnt
 * @rx_ht:	RX hold time on SDA in ic clock cycle cnt
 * @st:		setup time on SDA in ic clock cycle cnt
 */
void csp_i2c_set_timing(void *base_addr, u32 tx_ht, u32 rx_ht, u32 st)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_sda_timing_t sda_timing_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,tx_ht=%d,rx_ht=%d,st=%d\n",
			base_addr, tx_ht, rx_ht, st);

	sda_timing_mask.val = READREG32(&i2cc->sda_timing);
	sda_timing_mask.bits.sda_tx_hold = tx_ht;
	sda_timing_mask.bits.sda_rx_hold = rx_ht;
	sda_timing_mask.bits.sda_setup = st;
	WRITEREG32(&i2cc->sda_timing, sda_timing_mask.val);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * interrput enable/pending/clear
 *****************************************************************************/

/**
 * csp_i2c_enable_ints - enable the specified interrputs
 * @base_addr:	base address for I2CC
 * @int_mask:	int enable bits to be set
 */
void csp_i2c_enable_ints(void *base_addr, u32 int_mask)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	u32 int_en;

	PRT_TRACE_BEGIN("base_addr=0x%p,int_mask=0x%08x\n",
			base_addr, int_mask);

	int_en = READREG32(&i2cc->int_en);
	int_en |= int_mask;
	WRITEREG32(&i2cc->int_en, int_en);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_disable_ints - enable the specified interrputs
 * @base_addr:	base address for I2CC
 * @int_mask:	int enable bits to be set
 */
void csp_i2c_disable_ints(void *base_addr, u32 int_mask)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	u32 int_en;

	PRT_TRACE_BEGIN("base_addr=0x%p,int_mask=0x%08x\n",
			base_addr, int_mask);

	int_en = READREG32(&i2cc->int_en);
	int_en &= ~int_mask;
	WRITEREG32(&i2cc->int_en, int_en);

	PRT_TRACE_END("\n");
}

/* get int pending status */
u32 csp_i2c_get_int_pd(void *base_addr)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;

	PRT_TRACE_BEGIN("\n");
	PRT_TRACE_END("int_pd=0x%08x\n", READREG32(&i2cc->int_pd));
	return READREG32(&i2cc->int_pd);
}

/**
 * csp_i2c_clr_pd - clear int pending status
 * @base_addr:	base address for I2CC
 * @pd_mask:	int pending bits status to be clear
 */
void csp_i2c_clr_pd(void *base_addr, u32 pd_mask)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%p,pd_mask=0x%08x\n",
			base_addr, pd_mask);

	WRITEREG32(&i2cc->int_clr, pd_mask);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_chk_err - check whether there are error pendings occur
 * @base_addr:	base address for I2CC
 *
 * return 0 when not error, otherwise 1
 */
u32 csp_i2c_chk_err(void *base_addr)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	u32 int_pd_tmp = 0;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%p\n", base_addr);

	int_pd_tmp = READREG32(&i2cc->int_pd);
	/* report error when any error pending bit occur */
	if ((int_pd_tmp & I2C_INT_ALL_ERR) != 0) {
		PRT_DBG("int_pd=0x%08x\n", int_pd_tmp);
		ret = 1;
	}

	PRT_TRACE_END("int_pd=0x%08x\n", int_pd_tmp);
	return ret;
}

/**
 * csp_i2c_chk_pd - check whether all the expected int pending have been raise
 * @base_addr:	base address for I2CC
 * @pd_mask:	int pending bits to be check
 *
 * return 1 when all the expected pending bits are set, otherwise 0
 */
u32 csp_i2c_chk_pd(void *base_addr, u32 pd_mask)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	u32 int_pd_tmp;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%p,pd_mask=0x%08x\n",
			base_addr, pd_mask);

	int_pd_tmp = READREG32(&i2cc->int_pd);

	/* return 1 only when all bits in pd_mask are set */
	if ((int_pd_tmp & pd_mask) == pd_mask)
		ret = 1;

	PRT_TRACE_END("int_pd=0x%08x\n", int_pd_tmp);
	return ret;
}

/**
 * csp_i2c_catch_pd - check whether one of the expected int pending have been raise
 * @base_addr:	base address for I2CC
 * @pd_mask:	int pending bits to be check
 *
 * return 1 when one of the expected pending bits are set, otherwise 0
 */
u32 csp_i2c_catch_pd(void *base_addr, u32 pd_mask)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	u32 int_pd_tmp;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%p,pd_mask=0x%08x\n",
			base_addr, pd_mask);

	int_pd_tmp = READREG32(&i2cc->int_pd);

	/* return 1 only when one bit of pd_mask are set */
	if ((int_pd_tmp & pd_mask) != 0)
		ret = 1;

	PRT_TRACE_END("int_pd=0x%08x\n", int_pd_tmp);
	return ret;
}

/******************************************************************************
 * status
 *****************************************************************************/

/**
 * csp_i2c_chk_fifo_status - check whether all the expected
 *	status have been present
 * @base_addr:	base address for I2CC
 * @fifo_status:	expected status
 *
 * return 1 when all the expected status bits are set, otherwise 0
 */
u32 csp_i2c_chk_fifo_status(void *base_addr, u32 fifo_status)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	u32 status_tmp;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%p,fifo_status=0x%08x\n",
			base_addr, fifo_status);

	status_tmp = READREG32(&i2cc->fifo_status);

	/* return 1 only when all bits in pd_mask are set */
	if ((status_tmp & fifo_status) == fifo_status)
		ret = 1;

	PRT_TRACE_END("status_tmp=0x%08x\n", status_tmp);
	return ret;
}

/**
 * csp_i2c_chk_status - check whether all the expected status have been present
 * @base_addr:	base address for I2CC
 * @status:	expected status
 *
 * return 1 when all the expected status bits are set, otherwise 0
 */
u32 csp_i2c_chk_status(void *base_addr, u32 status)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	u32 status_tmp;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%p,status=0x%08x\n",
			base_addr, status);

	status_tmp = READREG32(&i2cc->status);

	/* return 1 only when all bits in pd_mask are set */
	if ((status_tmp & status) == status)
		ret = 1;

	PRT_TRACE_END("status_tmp=0x%08x\n", status_tmp);
	return ret;
}

/******************************************************************************
 * abort source
 *****************************************************************************/
/**
 * csp_i2c_get_abrt - get the abort sources
 * @base_addr:	base address for I2CC
 *
 * return bit masks of abort sources
 */
u32 csp_i2c_get_abrt(void *base_addr)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	u32 abrtsrc = 0;

	PRT_TRACE_BEGIN("base_addr=0x%p\n", base_addr);

	abrtsrc = READREG32(&i2cc->tx_abt_source);

	PRT_TRACE_END("abrtsrc=0x%08x\n", abrtsrc);
	return abrtsrc;
}

/**
 * csp_i2c_clr_tx_abrt - clear the abort source
 * @base_addr:	base address for I2CC
 * @abrtsrc:	abort source to be clear
 */
void csp_i2c_clr_tx_abrt(void *base_addr, u32 abrtsrc)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%p,abrtsrc=0x%08x\n",
			base_addr, abrtsrc);

	WRITEREG32(&i2cc->tx_abt_source, abrtsrc);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * transfer configure
 *****************************************************************************/

/**
 * csp_i2c_set_fifo_thresh - set TX & RX FIFO threshold
 * @base_addr:	base address for I2CC
 * @tx_th:	TX_FIFO threshold
 * @rx_th:	RX_FIFO threshold
 */
void csp_i2c_set_fifo_thresh(void *base_addr, u32 tx_th, u32 rx_th)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_fifo_ctl_t fifo_ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,tx_th=%d,rx_th=%d\n",
			base_addr, tx_th, rx_th);

	fifo_ctl_mask.val = READREG32(&i2cc->fifo_ctl);
	fifo_ctl_mask.bits.txth = tx_th;
	fifo_ctl_mask.bits.rxth = rx_th;
	WRITEREG32(&i2cc->fifo_ctl, fifo_ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_rt_fifo_reset - set TX & RX FIFO reset
 * @base_addr:	base address for I2CC
 * @en:	1:reset, 0 dis
 */
void csp_i2c_rt_fifo_reset(void *base_addr, u32 rx_reset, u32 tx_reset)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_fifo_ctl_t fifo_ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,rx_reset=%d,tx_reset=%d\n",
			base_addr, rx_reset, tx_reset);

	fifo_ctl_mask.val = READREG32(&i2cc->fifo_ctl);
	fifo_ctl_mask.bits.rxrst = rx_reset;
	fifo_ctl_mask.bits.txrst = tx_reset;
	WRITEREG32(&i2cc->fifo_ctl, fifo_ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_set_auto_reg_addr - set the device register addr
 * @base_addr:	base address for I2CC
 * @reg:	device register address
 * @len:	register length
 */
void csp_i2c_set_auto_reg_addr(void *base_addr, u32 reg, u32 len)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_ctl_t ctl_mask;
	reg_i2c_rar_t rar_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,reg=0x%08x,len=0x%03x\n",
			base_addr, reg, len);

	rar_mask.val = READREG32(&i2cc->rar);
	rar_mask.bits.rar = reg;
	WRITEREG32(&i2cc->rar, rar_mask.val);

	ctl_mask.val = READREG32(&i2cc->ctl);
	ctl_mask.bits.auto_rar_len = len;
	WRITEREG32(&i2cc->ctl, ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_set_auto_rw - set the device address
 * @base_addr:	base address for I2CC
 * @rw:		read-write bit, 0 write, 1 read
 */
void csp_i2c_set_auto_rw(void *base_addr, u32 rw)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,rw=0x%01x\n",
			base_addr, rw);

	ctl_mask.val = READREG32(&i2cc->ctl);
	ctl_mask.bits.auto_dir = rw;
	WRITEREG32(&i2cc->ctl, ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_set_rt_len - set the rt data len(byte)
 * @base_addr:	base address for I2CC
 * @rlen:	RX byte length
 * @tlen:	Tx byte length
 */
void csp_i2c_set_rt_len(void *base_addr, u16 rlen, u16 tlen)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_len_t len_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,rlen=0x%04x,tlen=0x%04x\n",
			base_addr, rlen, tlen);

	len_mask.val = READREG32(&i2cc->len);
	len_mask.bits.rx_len = rlen;
	len_mask.bits.tx_len = tlen;
	WRITEREG32(&i2cc->len, len_mask.val);

	PRT_TRACE_END("\n");
}
/**
 * csp_i2c_set_tar_addr - set the target address
 * @base_addr:	base address for I2CC
 * @addr:	target address (slave address to access)
 * @use_10bit:	use 10bit address
 */
void csp_i2c_set_tar_addr(void *base_addr, u16 addr, u32 use_10bit)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_dar_t dar_mask;
	reg_i2c_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,addr=0x%04x,use_10bit=%d\n",
			base_addr, addr, use_10bit);

	ctl_mask.val = READREG32(&i2cc->ctl);
	ctl_mask.bits.dev_addr_len = use_10bit;
	WRITEREG32(&i2cc->ctl, ctl_mask.val);

	dar_mask.val = READREG32(&i2cc->dar);
	dar_mask.bits.dar = addr;
	WRITEREG32(&i2cc->dar, dar_mask.val);

	PRT_TRACE_END("\n");
}
/**
 * csp_i2c_set_dev_addr - set the device address
 * @base_addr:	base address for I2CC
 * @addr:	device address (slave address to access)
 * @use_10bit:	use 10bit address
 */
void csp_i2c_set_dev_addr(void *base_addr, u16 addr, u32 use_10bit)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_dar_t dar_mask;
	reg_i2c_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,addr=0x%04x,use_10bit=%d\n",
			base_addr, addr, use_10bit);

	ctl_mask.val = READREG32(&i2cc->ctl);
	ctl_mask.bits.dev_addr_len = use_10bit;
	WRITEREG32(&i2cc->ctl, ctl_mask.val);

	dar_mask.val = READREG32(&i2cc->dar);
	dar_mask.bits.dar = addr;
	WRITEREG32(&i2cc->dar, dar_mask.val);

	PRT_TRACE_END("\n");
}


/******************************************************************************
 * transfer function
 *****************************************************************************/

/**
 * csp_i2c_send_cmd - send read/write cmd to TX_FIFO
 * @base_addr:	base address for I2CC
 * @cmd:	transfer direction, I2C_CMD_WRITE or I2C_CMD_READ
 * @data:	byte to transfer if I2C_CMD_WRITE, or ignored
 * @stop:	whether to send STOP after this byte
 * @restart:	whether to send RESTART before this byte
 */
void csp_i2c_send_cmd(void *base_addr, u32 cmd, u32 data,
			u32 stop, u32 restart)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_data_cmd_t data_cmd_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,cmd=%d,data=0x%02x,\n\t"
			"stop=%d,restart=%d\n",
			base_addr, cmd, data, stop, restart);

	/* Note: should not read the data_cmd register here,
	 *   reading the register will cause fetching data from RX_FIFO
	 *   and then cause fifo under run.
	 */
	data_cmd_mask.val = 0;
	data_cmd_mask.bits.data = data;
	data_cmd_mask.bits.cmd = cmd;
	data_cmd_mask.bits.stop = stop;
	data_cmd_mask.bits.restart = restart;
	WRITEREG32(&i2cc->data_cmd, data_cmd_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_get_rx_fl - get RX_FIFO fill level
 * @base_addr:	base address for I2CC
 *
 * return RX_FIFO fill level
 */

u32 csp_i2c_get_rx_fl(void *base_addr)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_fifo_status_t fifo_status_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p\n", base_addr);

	fifo_status_mask.val = READREG32(&i2cc->fifo_status);

	PRT_TRACE_END("rx_fl=%d\n", fifo_status_mask.bits.rx_fl);

	return fifo_status_mask.bits.rx_fl;
}

/**
 * csp_i2c_write_byte - write one byte from TX_FIFO(auto mode)
 * @base_addr:	base address for I2CC
 */
void csp_i2c_write_byte(void *base_addr, u8 data)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_data_cmd_t data_cmd_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,data=0x%02x,\n",
			base_addr, data);

	/* Note: should not read the data_cmd register here,
	 *   reading the register will cause fetching data from RX_FIFO
	 *   and then cause fifo under run.
	 */
	data_cmd_mask.val = 0;
	data_cmd_mask.bits.data = data;
	WRITEREG32(&i2cc->data_cmd, data_cmd_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_read_byte - read one byte from RX_FIFO
 * @base_addr:	base address for I2CC
 *
 * return value of the read byte
 */
u8 csp_i2c_read_byte(void *base_addr)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_data_cmd_t data_cmd_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p\n", base_addr);

	data_cmd_mask.val = READREG32(&i2cc->data_cmd);

	PRT_TRACE_END("val=%d\n", data_cmd_mask.bits.data);

	return (u8)data_cmd_mask.bits.data;
}

#if 0
/**
 * csp_i2c_read_byte - read one byte from RX_FIFO
 * @base_addr:	base address for I2CC
 *
 * return value of the read byte
 */
void csp_i2c_cfg_rx_dma(void *base_addr, u32 rx_th, u32 rx_len)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_dma_ctl_t dma_ctl_mask;
	reg_i2c_dma_len_t dma_len_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,rx_th=%d,rx_len=%d\n",
			base_addr, rx_th, rx_len);

	dma_ctl_mask.val = READREG32(&i2cc->dma_ctl);
	dma_ctl_mask.bits.dma_rdl = rx_th;
	WRITEREG32(&i2cc->dma_ctl, dma_ctl_mask.val);

	dma_len_mask.val = READREG32(&i2cc->dma_len);
	dma_len_mask.bits.rx_dma_len = rx_len;
	WRITEREG32(&i2cc->dma_len, dma_len_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_read_byte - read one byte from RX_FIFO
 * @base_addr:	base address for I2CC
 *
 * return value of the read byte
 */
void csp_i2c_cfg_tx_dma(void *base_addr, u32 tx_th, u32 tx_len)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_dma_ctl_t dma_ctl_mask;
	reg_i2c_dma_len_t dma_len_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,tx_th=%d,tx_len=%d\n",
			base_addr, tx_th, tx_len);

	dma_ctl_mask.val = READREG32(&i2cc->dma_ctl);
	dma_ctl_mask.bits.dma_rdl = tx_th;
	WRITEREG32(&i2cc->dma_ctl, dma_ctl_mask.val);

	dma_len_mask.val = READREG32(&i2cc->dma_len);
	dma_len_mask.bits.tx_dma_len = tx_len;
	WRITEREG32(&i2cc->dma_len, dma_len_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_enable_dma_rx - whether to enable DMA receive
 * @base_addr:	base address for I2CC
 * @enable:	0, disable; 1, enable
 */
void csp_i2c_enable_dma_rx(void *base_addr, u32 enable)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_dma_ctl_t dma_ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n",
			base_addr, enable);

	dma_ctl_mask.val = READREG32(&i2cc->dma_ctl);
	dma_ctl_mask.bits.tdmae = enable;
	WRITEREG32(&i2cc->dma_ctl, dma_ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2c_write_data - write one byte from master to slave
 * @base_addr:	base address for I2CC
 * @enable:	0, disable; 1, enable
 */
void csp_i2c_enable_dma_tx(void *base_addr, u32 enable)
{
	reg_i2c_t *i2cc = (reg_i2c_t *)base_addr;
	reg_i2c_dma_ctl_t dma_ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n",
			base_addr, enable);

	dma_ctl_mask.val = READREG32(&i2cc->dma_ctl);
	dma_ctl_mask.bits.rdmae = enable;
	WRITEREG32(&i2cc->dma_ctl, dma_ctl_mask.val);

	PRT_TRACE_END("\n");
}
#endif

/* dumpd I2CC registers for debug */
void csp_i2c_dumpregs(void *base_addr)
{
	u32 offset = 0;

	PRT_DBG(" =========== REGISTER DUMP (0x%p) ===========\n", base_addr);

	for (offset = 0; offset < 0xC; offset += 4) {
		PRT_DBG(" 0x%x: 0x%08x\n",
			offset, READREG32((unsigned long)base_addr + offset));
	}

	for (offset = 0x10; offset < 0x64; offset += 8) {
		PRT_DBG(" 0x%x: 0x%08x | 0x%x: 0x%08x\n",
			offset, READREG32((unsigned long)base_addr  + offset),
			offset + 4,
			READREG32((unsigned long)base_addr + offset + 4));
	}

	PRT_DBG(" ===========================================\n");
}
#ifdef CONFIG_ARCH_LOMBO_N7V3

#define i2coffsetof(MEMBER) ((size_t) &((reg_i2c4_t *)0)->MEMBER)

reg_i2c4_t g_i2c_cqi_cfg;

reg_i2c4_t *get_cqi_reg(void)
{
	return &g_i2c_cqi_cfg;
}

u32 set_dis_auto_i2c(void)
{
	reg_i2c4_t *i2c_cfg = get_cqi_reg();

	i2c_cfg->enable.bits.en = 0;
	i2c_cfg->enable.bits.auto_start = 0;

	return i2c_cfg->enable.val;
}

void get_i2coffsetof(u32 reg_offset, u32 *reg, u32 *val)
{
	reg_i2c4_t *i2c_cfg = get_cqi_reg();

	switch (reg_offset) {
	case 0:
		*reg = i2coffsetof(data_cmd);
		*val = i2c_cfg->len.val;
		break;
	case 1:
		*reg = i2coffsetof(int_pd);
		break;
	case 2:
		*reg = i2coffsetof(len);
		*val = i2c_cfg->len.val;
		break;
	case 3:
		*reg = i2coffsetof(tx_abt_source);
		*val = i2c_cfg->tx_abt_source.val;
		break;
	case 4:
		*reg = i2coffsetof(int_clr);
		*val = i2c_cfg->int_clr.val;
		break;
	case 5:
		*reg = i2coffsetof(ctl);
		*val = i2c_cfg->ctl.val;
		break;
	case 6:
		*reg = i2coffsetof(dar);
		*val = i2c_cfg->dar.val;
		break;
	case 7:
		*reg = i2coffsetof(enable);
		*val = i2c_cfg->enable.val;
		break;
	default:
		PRT_ERR("reg_offset out of range!");
		break;
	}

}

void csp_i2c_transmit_cfg(u32 tx_len, u32 dev_addr)
{
	reg_i2c4_t *i2c_cfg = get_cqi_reg();

	/* tx len */
	i2c_cfg->len.bits.tx_len = tx_len;

	/* clear abt */
	i2c_cfg->tx_abt_source.val = I2C_TX_ABTSRC_ALL_MASK;

	/* clear pd */
	i2c_cfg->int_clr.val = I2C_INT_ALL_MASK;

	/* enable auto mode */
	i2c_cfg->ctl.bits.auto_en = 1;

	/* transfer configure */
	/* set dev address */
	i2c_cfg->ctl.bits.dev_addr_len = 0;
	i2c_cfg->dar.bits.dar = dev_addr;

	/* set reg address */
	i2c_cfg->ctl.bits.auto_rar_len = 0;
	i2c_cfg->rar.bits.rar = 0;

	/* set auto dir r/w */
	i2c_cfg->ctl.bits.auto_dir = 0;

	/* enable_i2c */
	i2c_cfg->enable.bits.en = 1;

	/* enable auto mode start */
	i2c_cfg->enable.bits.auto_start = 1;

}

/**
 * csp_i2ccqi_enable_cs - enable the sau i2c4 controller switch
 * @int_mask:	int enable bits to be set
 */
void csp_i2ccqi_enable_cs(u32 int_mask)
{
	WRITEREG32(0xf1204070, int_mask);
}

/**
 * csp_i2ccqi_enable_ints - enable the specified interrputs
 * @base_addr:	base address for I2C4
 * @int_mask:	int enable bits to be set
 */
void csp_i2ccqi_enable_ints(void *base_addr, u32 int_mask)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;
	u32 int_en;

	PRT_TRACE_BEGIN("base_addr=0x%p,int_mask=0x%08x\n",
			base_addr, int_mask);

	int_en = READREG32(&i2cc->cqi_inten);
	int_en |= int_mask;
	WRITEREG32(&i2cc->cqi_inten, int_en);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2ccqi_disable_ints - enable the specified interrputs
 * @base_addr:	base address for I2C4
 * @int_mask:	int enable bits to be set
 */
void csp_i2ccqi_disable_ints(void *base_addr, u32 int_mask)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;
	u32 int_en;

	PRT_TRACE_BEGIN("base_addr=0x%p,int_mask=0x%08x\n",
			base_addr, int_mask);

	int_en = READREG32(&i2cc->cqi_inten);
	int_en &= ~int_mask;
	WRITEREG32(&i2cc->cqi_inten, int_en);

	PRT_TRACE_END("\n");
}
/**
 * csp_i2ccqi_clr_pd - clear int pending status
 * @base_addr:	base address for I2C4
 * @pd_mask:	int pending bits status to be clear
 */
void csp_i2ccqi_clr_pd(void *base_addr, u32 pd_mask)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%p,pd_mask=0x%08x\n",
			base_addr, pd_mask);

	WRITEREG32(&i2cc->cqi_intclr, pd_mask);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2ccqi_get_int_pd - get int pending
 * @base_addr:	base address for I2C4
 * return:	int pending status
 */
u32 csp_i2ccqi_get_int_pd(void *base_addr)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;

	PRT_TRACE_BEGIN("\n");
	PRT_TRACE_END("int_pd=0x%08x\n", READREG32(&i2cc->cqi_intpd));
	return READREG32(&i2cc->cqi_intpd);
}
/**
 * csp_i2ccqi_set_addr - set buf first addr
 * @base_addr:	base address for I2C4
 * @addr:	buf addr
 */
void csp_i2ccqi_set_addr(void *base_addr, u32 addr)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;
	reg_i2c4_cqi_base_addr_t addr_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, addr);

	addr_mask.val = READREG32(&i2cc->cqi_base_addr);
	addr_mask.bits.addr = addr;

	WRITEREG32(&i2cc->cqi_base_addr, addr_mask.val);

	PRT_TRACE_END("\n");
}
/**
 * csp_i2ccqi_set_addr_len - set first list len
 * @base_addr:	base address for I2C4
 * @len:	first list len
 */
void csp_i2ccqi_set_addr_len(void *base_addr, u32 len)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;
	reg_i2c4_cqi_base_len_t len_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, len);

	len_mask.val = READREG32(&i2cc->cqi_base_len);
	len_mask.bits.len = len;

	WRITEREG32(&i2cc->cqi_base_len, len_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2ccqi_set_reset - set reset status
 * @base_addr:	base address for I2C4
 * @enable:	0, reset; 1, normal
 */
void csp_i2ccqi_set_reset(void *base_addr, u32 enable)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;
	reg_i2c4_cqi_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, enable);

	ctl_mask.val = READREG32(&i2cc->cqi_ctl);
	ctl_mask.bits.rst = enable;

	WRITEREG32(&i2cc->cqi_ctl, ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2ccqi_start_enable - enable/disable the i2c4 start
 * @base_addr:	base address for I2C4
 * @enable:	0, idle; 1, run
 */
void csp_i2ccqi_start_enable(void *base_addr, u32 enable)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;
	reg_i2c4_cqi_ctl_t ctl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%p,enable=%d\n", base_addr, enable);

	ctl_mask.val = READREG32(&i2cc->cqi_ctl);
	ctl_mask.bits.start = enable;

	WRITEREG32(&i2cc->cqi_ctl, ctl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_i2ccqi_chk_status - check whether all the expected status
 *	have been present
 * @base_addr:	base address for I2C4
 * @status:	expected status
 *
 * return 1 when all the expected status bits are set, otherwise 0
 */
u32 csp_i2ccqi_chk_status(void *base_addr, u32 status)
{
	reg_i2c4_t *i2cc = (reg_i2c4_t *)base_addr;
	u32 status_tmp;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%p,status=0x%08x\n",
			base_addr, status);

	status_tmp = READREG32(&i2cc->cqi_sta);

	/* return 1 only when all bits in pd_mask are set */
	if ((status_tmp & status) == status)
		ret = 1;

	PRT_TRACE_END("status_tmp=0x%08x\n", status_tmp);
	return ret;
}
#endif

#endif /* ___I2C___CSP__C___ */
